package com.naiterui.ehp.bp.utils.biz;

import java.util.HashMap;

import org.json.JSONObject;

import com.baidu.aip.ocr.AipOcr;

/**
 * 身份证OCR识别(百度)
 * @author guoyongxiang
 * @date 2018/11/20 17:21
 * @since 1.0.0
 */
public class IdCardOCRUtil {

    private static final String APP_ID = "14877270";
    private static final String API_KEY = "bCga8mongM9aVTH0UnwVuHNm";
    private static final String SECRET_KEY = "wzA5aqDoNaLEGKaxpGN12tHKMQlERwPY";

    private static AipOcr client;


    public static AipOcr getInstance(){
        if(client == null){
            client = new AipOcr(APP_ID, API_KEY, SECRET_KEY);
            client.setConnectionTimeoutInMillis(1000);
            client.setSocketTimeoutInMillis(10000);
        }
        return client;
    }

    /**
     * 身份证图片OCR识别
     * @param path
     * @param front
     * @return
     * @throws BaiduOcrException
     */
    public static OcrVO ocr(String path, boolean front) throws BaiduOcrException {
        AipOcr client = getInstance();
        return ocr(client, path, front);
    }

    /**
     * 身份证图片OCR识别
     * @param client
     * @param path
     * @param front
     * @return
     * @throws BaiduOcrException
     */
    public static OcrVO ocr(AipOcr client, String path, boolean front) throws BaiduOcrException {
        HashMap<String, String> options = new HashMap<>();
        // 检测朝向
        options.put("detect_direction", "true");
        // 风险检测
        options.put("detect_risk", "true");

        // 正反面
        String idCardSide = "front";
        if(!front){
            idCardSide = "back";
        }
        JSONObject res = client.idcard(path, idCardSide, options);
        boolean error = res.isNull("error_code");
        if(!error){
            // 本地SDK错误码为String类型，服务端错误码为int类型
            String code = res.get("error_code") + "";
            String msg = res.getString("error_msg");
            throw new BaiduOcrException(code, msg);
        }

        Long logId = res.getLong("log_id");
        Integer direction = res.getInt("direction");
        // 图像状态
        String imageStatus = res.getString("image_status");
        String riskType = res.getString("risk_type");

        JSONObject words = res.getJSONObject("words_result");

        OcrVO ocr = new OcrVO(logId + "", direction, imageStatus, getImageStatus(imageStatus), riskType, getRiskType(riskType));
        if(front){
            String address = words.isNull("住址") ? "" : words.getJSONObject("住址").getString("words");
            String number = words.isNull("公民身份号码") ? "" : words.getJSONObject("公民身份号码").getString("words");
            String birthday = words.isNull("出生") ? "" : words.getJSONObject("出生").getString("words");
            String name = words.isNull("姓名") ? "" : words.getJSONObject("姓名").getString("words");
            String gender = words.isNull("性别") ? "" : words.getJSONObject("性别").getString("words");
            String nation = words.isNull("民族") ? "" : words.getJSONObject("民族").getString("words");
            ocr.setFont(new FrontVO(number, name, address, birthday, gender, nation));
        }else{
            String nvalidiDate = words.isNull("失效日期") ? "" : words.getJSONObject("失效日期").getString("words");
            String signingAuthority = words.isNull("签发机关") ? "" : words.getJSONObject("签发机关").getString("words");
            String signingDate = words.isNull("签发日期") ? "" :  words.getJSONObject("签发日期").getString("words");
            ocr.setBack(new BackVO(nvalidiDate, signingAuthority, signingDate));
        }
        return ocr;
    }

    /**
     * 图片识别状态对照
     * @param imageStatus
     * @return
     */
    private static String getImageStatus(String imageStatus){
        for(ImageStatusEnum statusEnum : ImageStatusEnum.values()){
            if(statusEnum.getStatus().equals(imageStatus)){
                return statusEnum.getDesc();
            }
        }
        return imageStatus;
    }

    /**
     * 风险类别对照
     * @param riskType
     * @return
     */
    private static String getRiskType(String riskType){
        for(RiskTypeEnum typeEnum : RiskTypeEnum.values()){
            if(typeEnum.getType().equals(riskType)){
                return typeEnum.getDesc();
            }
        }
        return riskType;
    }

    /**
     * 图片状态枚举类
     */
    public enum ImageStatusEnum {
        NORMAL("normal", "识别正常"),
        REVERSED_SIDE("reversed_side", "身份证正反面颠倒"),
        NON_IDCARD("non_idcard", "上传的图片中不包含身份证"),
        BLURRED("blurred", "身份证模糊"),
        OTHER_TYPE_CARD("other_type_card", "其他证件类型"),
        OVER_EXPOSURE("over_exposure", "身份证关键字段反光或过曝"),
        OVER_DARK("over_dark", "身份证欠曝（亮度过低）"),
        UNKNOWN("unknown", "未知状态"),
        ;

        ImageStatusEnum(String status, String desc) {
            this.status = status;
            this.desc = desc;
        }
        private String status;
        private String desc;

        public String getStatus() {
            return this.status;
        }
        public String getDesc() {
            return this.desc;
        }
    }

    /**
     * 风险类型枚举类
     */
    public enum RiskTypeEnum {
        NORMAL("normal", "正常身份证"),
        COPY("copy", "复印件"),
        TEMPORARY("temporary", "临时身份证"),
        SCREEN("screen", "翻拍"),
        UNKNOWN("unknown", "未知类型"),
        ;

        RiskTypeEnum(String type, String desc) {
            this.type = type;
            this.desc = desc;
        }
        private String type;
        private String desc;

        public String getType() {
            return this.type;
        }
        public String getDesc() {
            return this.desc;
        }
    }

    /**
     * OCR识别结果VO
     */
    public static class OcrVO {
        /** 记录ID */
        private String logId;
        /** 图片方向 */
        private Integer direction;
        /** 识别结果 */
        private String imageStatus;
        /** 识别结果描述 */
        private String imageStatusDesc;
        /** 风险类型 */
        private String riskType;
        /** 风险类型描述 */
        private String riskTypeDesc;
        /** 正面信息 */
        private FrontVO font;
        /** 反面信息 */
        private BackVO back;

        public OcrVO(String logId, Integer direction, String imageStatus, String imageStatusDesc, String riskType, String riskTypeDesc) {
            this.logId = logId;
            this.direction = direction;
            this.imageStatus = imageStatus;
            this.imageStatusDesc = imageStatusDesc;
            this.riskType = riskType;
            this.riskTypeDesc = riskTypeDesc;
        }

        public String getLogId() {
            return this.logId;
        }

        public void setLogId(String logId) {
            this.logId = logId;
        }

        public Integer getDirection() {
            return this.direction;
        }

        public void setDirection(Integer direction) {
            this.direction = direction;
        }

        public String getImageStatus() {
            return this.imageStatus;
        }

        public void setImageStatus(String imageStatus) {
            this.imageStatus = imageStatus;
        }

        public String getImageStatusDesc() {
            return this.imageStatusDesc;
        }

        public void setImageStatusDesc(String imageStatusDesc) {
            this.imageStatusDesc = imageStatusDesc;
        }

        public String getRiskType() {
            return this.riskType;
        }

        public void setRiskType(String riskType) {
            this.riskType = riskType;
        }

        public String getRiskTypeDesc() {
            return this.riskTypeDesc;
        }

        public void setRiskTypeDesc(String riskTypeDesc) {
            this.riskTypeDesc = riskTypeDesc;
        }

        public FrontVO getFont() {
            return this.font;
        }

        public void setFont(FrontVO font) {
            this.font = font;
        }

        public BackVO getBack() {
            return this.back;
        }

        public void setBack(BackVO back) {
            this.back = back;
        }
    }

    /**
     * 身份证正面信息
     */
    public static class FrontVO {
        /** 身份证号码 */
        private String number;
        /** 姓名 */
        private String name;
        /** 地址 */
        private String address;
        /** 生日(yyyyMMdd) */
        private String birthday;
        /** 性别 */
        private String gender;
        /** 民族 */
        private String nation;

        public FrontVO(String number, String name, String address, String birthday, String gender, String nation) {
            this.number = number;
            this.name = name;
            this.address = address;
            this.birthday = birthday;
            this.gender = gender;
            this.nation = nation;
        }

        public String getNumber() {
            return this.number;
        }

        public void setNumber(String number) {
            this.number = number;
        }

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAddress() {
            return this.address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public String getBirthday() {
            return this.birthday;
        }

        public void setBirthday(String birthday) {
            this.birthday = birthday;
        }

        public String getGender() {
            return this.gender;
        }

        public void setGender(String gender) {
            this.gender = gender;
        }

        public String getNation() {
            return this.nation;
        }

        public void setNation(String nation) {
            this.nation = nation;
        }
    }

    /**
     * 身份证背面信息
     */
    public static class BackVO {
        /** 失效日期(yyyyMMdd) */
        private String nvalidiDate;
        /** 签发地 */
        private String signingAuthority;
        /** 签发日期(yyyyMMdd) */
        private String signingDate;

        public BackVO(String nvalidiDate, String signingAuthority, String signingDate) {
            this.nvalidiDate = nvalidiDate;
            this.signingAuthority = signingAuthority;
            this.signingDate = signingDate;
        }

        public String getNvalidiDate() {
            return this.nvalidiDate;
        }

        public void setNvalidiDate(String nvalidiDate) {
            this.nvalidiDate = nvalidiDate;
        }

        public String getSigningAuthority() {
            return this.signingAuthority;
        }

        public void setSigningAuthority(String signingAuthority) {
            this.signingAuthority = signingAuthority;
        }

        public String getSigningDate() {
            return this.signingDate;
        }

        public void setSigningDate(String signingDate) {
            this.signingDate = signingDate;
        }
    }

    /**
     * 百度OCR API错误
     */
    public static class BaiduOcrException extends Exception {

        private static final long serialVersionUID = 5732389679186703101L;
        private String code;

        public BaiduOcrException(String code, String msg) {
            super(msg);
        }

        public String getCode() {
            return this.code;
        }

        public void setCode(String code) {
            this.code = code;
        }
    }

    public enum ErrorEnum {

        CODE_SDK100("SDK100", "图片大小超限"),
        CODE_SDK101("SDK101", "图片边长不符合要求"),
        CODE_SDK102("SDK102", "读取图片文件错误"),
        CODE_SDK108("SDK108", "连接超时或读取数据超时"),
        CODE_SDK109("SDK109", "不支持的图片格式"),

        // 服务端错误码为int，为保证与客户端错误码类型统一转换成String
        CODE_4(4 + "", "集群超限额"),
        CODE_14(14 + "", "IAM鉴权失败"),
        CODE_17(17 + "", "每天流量超限额"),
        CODE_18(18 + "", "QPS超限额"),
        CODE_19(19+ "", "请求总量超限额"),
        CODE_100(100+ "", "无效参数"),
        CODE_110(110+ "", "Access Token失效"),
        CODE_111(111+ "", "Access token过期"),
        CODE_282000(282000+ "", "服务器内部错误"),
        CODE_216100(216100+ "", "请求中包含非法参数，请检查后重新尝试"),
        CODE_216101(216101+ "", "缺少必须的参数，请检查参数是否有遗漏"),
        CODE_216102(216102+ "", "请求了不支持的服务，请检查调用的url"),
        CODE_216103(216103+ "", "请求中某些参数过长，请检查后重新尝试"),
        CODE_216110(216110+ "", "appid不存在，请重新核对信息是否为后台应用列表中的appid"),
        CODE_216200(216200+ "", "图片为空，请检查后重新尝试"),
        CODE_216201(216201+ "", "上传的图片格式错误，现阶段我们支持的图片格式为：PNG、JPG、JPEG、BMP，请进行转码或更换图片"),
        CODE_216202(216202+ "", "上传的图片大小错误，现阶段我们支持的图片大小为：base64编码后小于4M，分辨率不高于4096*4096，请重新上传图片"),
        CODE_216630(216630+ "", "识别错误，请再次请求，如果持续出现此类错误，请联系技术支持"),
        CODE_216633(216633+ "", "识别身份证错误，出现此问题的原因一般为：您上传了非身份证图片或您上传的身份证图片不完整"),
        CODE_216634(216634+ "", "检测错误，请再次请求，如果持续出现此类错误，请联系技术支持"),
        CODE_282003(282003+ "", "请求参数缺失"),
        CODE_282810(282810+ "", "图像识别错误"),
        ;

        private String code;
        private String message;

        ErrorEnum(String code, String message) {
            this.code = code;
            this.message = message;
        }

        public String getCode() {
            return this.code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getMessage() {
            return this.message;
        }

        public void setMessage(String message) {
            this.message = message;
        }
    }

}
